using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.Collections.ObjectModel;

namespace MIMPGameLibrary
{
    /// <summary>
    /// Holds game logic and data. An instance of GameState contains all the information needed 
    /// to represent the game at a particular time. The class provides methods to update the game state in all 
    /// the ways needed by gameplay. GameState does not attempt to describe how the game should be graphically 
    /// presented except in certain high-level specifications, e.g. the size of the board and the position of 
    /// each monter on it.
    /// </summary>
    [Serializable]
    public class GameState
    {
        private Hex[,] _Hexes;
        private List<Hex> hexList;
        /// <summary>
        /// A two-dimensional array of Hex objects that represents the game board. To access the Hex at row r,
        /// column c, use "Hexes[r,c]".
        /// </summary>
        public Hex[,] Hexes {
            get { return _Hexes; }
        }
        public List<Hex> HexList {
            get {
                if (hexList == null) {
                    hexList = new List<Hex>();
                    foreach (Hex h in _Hexes) {
                        hexList.Add(h);
                    }
                    return hexList;
                } else {
                    return hexList;
                }
            }
        }
        public int ControlPointCount {
            get {
                int c = 0;
                foreach (Hex h in _Hexes) {
                    if (h.HasControlPoint) { c++; }
                }
                return c;
            }
        }
        private int rows;
        /// <summary>
        /// The number of rows of Hexes in the game board.
        /// </summary>
        public int Rows {
            get { return rows; }
        }
        private int cols;
        /// <summary>
        /// The number of columns of Hexes in the game board.
        /// </summary>
        public int Cols {
            get { return cols; }
        }
        private List<Player> _Players;
        /// <summary>
        /// A List of all the Players playing the game.
        /// </summary>
        public List<Player> Players {
            get { return _Players; }
        }
        private List<Monster> _Monsters;
        /// <summary>
        /// A List of all the Monsters in the game.
        /// </summary>
        public List<Monster> Monsters {
            get { return _Monsters; }
        }
        private int active;
        private int NextPlayer {
            get { return active == _Players.Count - 1 ? 0 : active + 1; }
        }
        /// <summary>
        /// The Player whose turn it currently is.
        /// </summary>
        public Player ActivePlayer {
            get { return _Players[active]; }
        }

        private Monster selected;
        /// <summary>
        /// The currently selected Monster.
        /// </summary>
        public Monster Selected {
            get { return selected; }
            set {
                if (value != null && !value.Controller.Equals(_Players[active])) {
                    throw new IllegalSelectionException(value.Name + " is not controlled by the active player (" + active + ")");
                }
                selected = value;
            }
        }

        private Monster target;

        public Monster Target {
            get { return target; }
            set {
                if (value != null && value.Controller == selected.Controller) {
                    throw new IllegalSelectionException("You cannot attack monsters under your control.");
                }
                target = value;
            }
        }

        private List<Monster> defenders;

        public List<Monster> Defenders {
            get { return defenders; }
            set { defenders = value; }
        }

        private List<Monster> attackers;

        public List<Monster> Attackers {
            get { return attackers; }
            set { attackers = value; }
        }

        private AttackResult attackResult;

        /// <summary>
        /// True if a Monster is currently selected, false otherwise.
        /// </summary>
        public bool IsSelected {
            get { return selected != null; }
        }
        /// <summary>
        /// Enumerates the phases of a game. Some methods of this class are only valid in a particular phase.
        /// </summary>
        public enum Phases { Setup, Play }
        private Phases phase;
        /// <summary>
        /// Gets the current game phase. Use BeginGame() to advance from Setup phase to Play phase.
        /// </summary>
        public Phases Phase {
            get { return phase; }
        }

        /// <summary>
        /// Constructs a new GameState with a board of the specified dimensions 
        /// and with the specified number of human players.
        /// </summary>
        /// <param name="rows">The number of rows in the board of Hexes.</param>
        /// <param name="cols">The number of columns in the board of Hexes.</param>
        /// <param name="players">The number of Players playing this game.</param>
        public GameState(int rows, int cols, int players) {
            if (players > 8) {
                throw new ArgumentException("Cannot have more than 8 players.");
            }
            this.rows = rows;
            this.cols = cols;
            this._Hexes = new Hex[rows, cols];

            Color[] colors = { Color.Red, Color.Blue, Color.Orange, Color.LimeGreen, Color.Purple, Color.Yellow, Color.Cyan, Color.Sienna };

            //Initialize all Hexes...
            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < cols; j++) {
                    Hexes[i, j] = new Hex(i, j);
                }
            }
            //...then set up their neighbors
            SetHexNeighbors();

            this._Players = new List<Player>(players);
            for (int i = 0; i < players; i++) {
                this._Players.Add(new Player("Player" + i, colors[i], 50));
            }
            this.active = 0;

            this._Monsters = new List<Monster>();
            this.selected = null;
            this.target = null;
            this.defenders = new List<Monster>();
            this.attackers = new List<Monster>();

            this.phase = Phases.Setup;
        }

        private void SetHexNeighbors() {
            foreach (Hex h in Hexes) {
                //Different rules for hexes in even vs odd columns
                if (h.Col % 2 == 1) {
                    try { h.SetNeighbor(Hex.Direction.N, Hexes[h.Row - 1, h.Col]); } catch (IndexOutOfRangeException) { }
                    try { h.SetNeighbor(Hex.Direction.NE, Hexes[h.Row, h.Col + 1]); } catch (IndexOutOfRangeException) { }
                    try { h.SetNeighbor(Hex.Direction.SE, Hexes[h.Row + 1, h.Col + 1]); } catch (IndexOutOfRangeException) { }
                    try { h.SetNeighbor(Hex.Direction.S, Hexes[h.Row + 1, h.Col]); } catch (IndexOutOfRangeException) { }
                    try { h.SetNeighbor(Hex.Direction.SW, Hexes[h.Row + 1, h.Col - 1]); } catch (IndexOutOfRangeException) { }
                    try { h.SetNeighbor(Hex.Direction.NW, Hexes[h.Row, h.Col - 1]); } catch (IndexOutOfRangeException) { }
                } else {
                    try { h.SetNeighbor(Hex.Direction.N, Hexes[h.Row - 1, h.Col]); } catch (IndexOutOfRangeException) { }
                    try { h.SetNeighbor(Hex.Direction.NE, Hexes[h.Row - 1, h.Col + 1]); } catch (IndexOutOfRangeException) { }
                    try { h.SetNeighbor(Hex.Direction.SE, Hexes[h.Row, h.Col + 1]); } catch (IndexOutOfRangeException) { }
                    try { h.SetNeighbor(Hex.Direction.S, Hexes[h.Row + 1, h.Col]); } catch (IndexOutOfRangeException) { }
                    try { h.SetNeighbor(Hex.Direction.SW, Hexes[h.Row, h.Col - 1]); } catch (IndexOutOfRangeException) { }
                    try { h.SetNeighbor(Hex.Direction.NW, Hexes[h.Row - 1, h.Col - 1]); } catch (IndexOutOfRangeException) { }
                }
            }
        }

        /// <summary>
        /// Adds a new Monster to the game.
        /// </summary>
        /// <param name="newMonster">The Monster to add.</param>
        /// <exception cref="ArgumentException">Thrown if the Player name specified by newMonster.Controller does not exist or the specified Player already has a Monster with this name.</exception>
        private void AddMonster(Monster newMonster) {
            Player foundPlayer = _Players.Find(delegate(Player p) { return p.Equals(newMonster.Controller); });
            if (foundPlayer == null) {
                throw new ArgumentException(newMonster.Controller + " is not a valid Player.");
            }
            Monster foundMonster = _Monsters.Find(delegate(Monster m) { return m.Name.Equals(newMonster.Name); });
            if (foundMonster != null) {
                throw new IllegalDeploymentException("A monster with name " + newMonster.Name + " already exists.");
            }
            _Monsters.Add(newMonster);
            foundPlayer.Monsters.Add(newMonster);
        }
        /// <summary>
        /// Deploys a new Monster to the game, which means adding it to the given Player's Monsters list and 
        /// deprecating the controller's DeploymentPoints by an amount equal to the new Monster's cost. Uses
        /// the new Monster's Location and Controller properties to determine the Hex to place it on and the Player
        /// to give control of it to.
        /// </summary>
        /// <param name="newMonster">The Monster to deploy.</param>
        public void DeployMonster(Monster newMonster) {
            this.DeployMonster(newMonster, this.ActivePlayer, newMonster.Location);
        }
        /// <summary>
        /// Deploys a new Monster to the game, which means adding it to the given Player's Monsters list and 
        /// deprecating the controller's DeploymentPoints by an amount equal to the new Monster's cost. Uses
        /// the GameState's ActivePlayer property to determine the Player to give control of it to.
        /// </summary>
        /// <param name="newMonster"></param>
        /// <param name="location"></param>
        public void DeployMonster(Monster newMonster, Hex location) {
            this.DeployMonster(newMonster, this.ActivePlayer, location);
        }
        /// <summary>
        /// Deploys a new Monster to the game, which means adding it to the given Player's Monsters list and 
        /// deprecating the controller's DeploymentPoints by an amount equal to the new Monster's cost. Uses
        /// the new Monster's Location property to determine the Hex to place it on.
        /// </summary>
        /// <param name="newMonster">The Monster to deploy.</param>
        /// <param name="controller">The Player who will have control of the new Monster.</param>
        public void DeployMonster(Monster newMonster, Player controller) {
            this.DeployMonster(newMonster, controller, newMonster.Location);
        }
        /// <summary>
        /// Deploys a new Monster to the game, which means adding it to the given Player's Monsters list and 
        /// deprecating the controller's DeploymentPoints by an amount equal to the new Monster's cost.
        /// </summary>
        /// <param name="newMonster">The Monster to deploy.</param>
        /// <param name="controller">The Player who will have control of the new Monster.</param>
        /// <param name="location">The Hex to place the new Monster on.</param>
        public void DeployMonster(Monster newMonster, Player controller, Hex location) {
            if (phase != Phases.Setup) { throw new InvalidOperationException("The DeployMonster method may only be called when the game phase is Phase.Setup."); }
            if (location.IsMonsterOn) { throw new IllegalDeploymentException("Hex " + location + " is already occupied."); }
            if (controller.DeploymentPoints < newMonster.Cost) { throw new IllegalDeploymentException("You do not have enough deployment points to deploy that monster!"); }
            if (newMonster.MovementCosts[location.Terrain] == 0) { throw new IllegalDeploymentException("Monsters of type " + newMonster.Type + " cannot move in " + Enum.GetName(typeof(Hex.TerrainType), location.Terrain) + "."); }

            newMonster.Controller = controller;
            newMonster.Location = location;
            this.AddMonster(newMonster);
            this.PlaceMonster(newMonster, location);
            controller.DeploymentPoints -= newMonster.Cost;
        }
        /// <summary>
        /// Removes the given monster from the playing field and refunds its controller the appropriate number of deployment points.
        /// </summary>
        /// <param name="toRemove">The Monster to remove.</param>
        public void UndeployMonster(Monster toRemove) {
            Player foundPlayer = _Players.Find(delegate(Player p) { return p.Equals(toRemove.Controller); });
            if (foundPlayer == null) {
                throw new ArgumentException(toRemove.Controller + " is not a valid Player.");
            }
            foundPlayer.DeploymentPoints += toRemove.Cost;
            this.RemoveMonster(toRemove);
        }

        /// <summary>
        /// Deletes a Monster from the game.
        /// </summary>
        /// <param name="toRemove">The Monster to remove.</param>
        private void RemoveMonster(Monster toRemove) {
            Monster foundMonster = _Monsters.Find(delegate(Monster m) { return m.Equals(toRemove); });
            if (foundMonster != null) {
                _Monsters.Remove(toRemove);
                toRemove.Controller.Monsters.Remove(toRemove);
                toRemove.Location.MonsterOn = null;
            }
        }
        /// <summary>
        /// Moves the specified Monster to the specified location on the board.
        /// </summary>
        /// <param name="monster">The Monster to place.</param>
        /// <param name="row">The row number of the Hex to place the Monster on.</param>
        /// <param name="col">The column number of the Hex to place the Monster on.</param>
        public void PlaceMonster(Monster monster, int row, int col) {
            Hex destination = Hexes[row, col];
            if (monster.Location != null) {
                monster.Location.MonsterOn = null;
            }
            monster.Location = destination;
            destination.MonsterOn = monster;
        }
        /// <summary>
        /// Moves the specified Monster to the specified location on the board.
        /// </summary>
        /// <param name="monster">The Monster to place.</param>
        /// <param name="location">The Hex to place the Monster on.</param>
        public void PlaceMonster(Monster monster, Hex location) {
            this.PlaceMonster(monster, location.Row, location.Col);
        }

        /// <summary>
        /// Adds the given monster to the list of defenders.
        /// </summary>
        /// <param name="monster">The Monser that will become a defender.</param>
        public void AddDefender(Monster monster) {
            if (!target.Controller.Equals(monster.Controller)) {
                throw new IllegalSelectionException(monster.Name + " is not controlled by the defending player (" + target.Controller + ")");
            } else if (defenders.Contains(monster)) {
                throw new IllegalSelectionException(monster.Name + " is already defending.");
            } else if (!monster.IsAlive) {
                throw new IllegalSelectionException(monster.Name + " is dead and cannot defend.");
            }
            //The monster may only defend if it is on a hex that is a neighbor to the hex of at least one attacker.
            bool legal = selected.Location.IsNeighbor(monster.Location);
            foreach (Monster m in attackers) {
                legal = legal || m.Location.IsNeighbor(monster.Location);
            }
            if (!legal) {
                throw new IllegalSelectionException(monster.Name + " is not close enough to help defend.");
            }
            defenders.Add(monster);
        }

        public void AddAttacker(Monster attacker) {
            if (selected == null) {
                throw new ArgumentException("Cannot add attackers while selected is null");
            } else if (selected.Controller != attacker.Controller) {
                throw new IllegalSelectionException(attacker.Name + " is not controlled by the attacking player (" + selected.Controller + ")");
            } else if (!attacker.IsAlive) {
                throw new IllegalSelectionException(attacker.Name + " is dead and cannot attack.");
            } else if (!attacker.Location.IsNeighbor(target.Location)) {
                throw new IllegalSelectionException(attacker.Name + " is not close enough to attack the target.");
            }

            attackers.Add(attacker);
        }

        /// <summary>
        /// Removes the specified Monster from the list of defending Monsters.
        /// </summary>
        public void RemoveDefender(Monster toRemove) {
            if (defenders.Contains(toRemove)) {
                defenders.Remove(toRemove);
            }
        }
        /// <summary>
        /// Removes the specified Monster from the list of attacking Monsters.
        /// </summary>
        /// <param name="toRemove"></param>
        public void RemoveAttacker(Monster toRemove) {
            if (attackers.Contains(toRemove)) {
                attackers.Remove(toRemove);
            }
        }

        /// <summary>
        /// Moves the currently selected Monster to the given Hex.
        /// </summary>
        /// <param name="hex"></param>
        public void Move(Hex hex) {
            this.Move(hex.Row, hex.Col);
        }

        /// <summary>
        /// Moves the currently selected Monster to the Hex specified by the given coordinates.
        /// </summary>
        /// <param name="row">The row number of the Hex to move to.</param>
        /// <param name="col">The column number of the Hex to move to.</param>
        /// <exception cref="IllegalMoveException">The selected Monster cannot reach the intended destination, or there is no selected Monster.</exception>
        /// <exception cref="InvalidOperationException">Thrown if this method is called outside the appropriate game phase.</exception>
        public void Move(int row, int col) {
            if (phase != Phases.Play) { throw new InvalidOperationException("The Move method may only be called when the game phase is Phase.Play."); }
            if (!IsSelected) {
                throw new IllegalMoveException("No monster is currently selected.");
            }
            Hex destination = Hexes[row, col];

            Hex origin = selected.Location;
            List<Hex> path = ShortestPath(origin, destination, selected);
            foreach (Hex step in path) {
                selected.MovesRemaining -= TakeStep(step);
            }
        }

        int TakeStep(Hex nextStep) {
            int cost = selected.MovementCosts[nextStep.Terrain];

            if (selected == null) {
                throw new IllegalMoveException("There is no monster selected.");
            } else if (selected.MovesRemaining < cost) {
                throw new IllegalMoveException(selected.Name + " needs at least " + cost + " movement points to move to " + nextStep + ".");
            } else if (cost == 0) {
                throw new IllegalMoveException(selected + " cannot move through " + Enum.GetName(typeof(Hex.TerrainType), nextStep.Terrain) + " hexes.");
            } else if (nextStep.IsMonsterOn) {
                throw new IllegalMoveException("Hex " + nextStep + " is already occupied.");
            } else if (!selected.IsAlive) {
                throw new IllegalMoveException("Monster " + selected.Name + " is dead and cannot move.");
            }
            PlaceMonster(selected, nextStep.Row, nextStep.Col);
            return cost;
        }
        /// <summary>
        /// Returns a list of Hexes that represent a shortest path between two Hexes, defined as a path
        /// that will cost the given Monster the fewest number of movement points. Implements Dijkstra's algorithm.
        /// </summary>
        /// <param name="source">The Hex to start on.</param>
        /// <param name="destination">The Hex to end on.</param>
        /// <param name="mover">The Monster to calculate the path for.</param>
        /// <returns>The shortest path (for mover) from source to destination.</returns>
        public List<Hex> ShortestPath(Hex source, Hex destination, Monster mover) {
            if (mover.MovementCosts[destination.Terrain] == 0) {
                throw new IllegalMoveException(selected + " cannot move through " + Enum.GetName(typeof(Hex.TerrainType), destination.Terrain) + " hexes.");
            }

            Dictionary<Hex, int> dist = new Dictionary<Hex, int>(_Hexes.Length);
            Dictionary<Hex, Hex> prev = new Dictionary<Hex, Hex>();
            List<Hex> copy = new List<Hex>();
            foreach (Hex h in _Hexes) {
                dist.Add(h, int.MaxValue);
                prev.Add(h, null);
                copy.Add(h);
            }
            dist[source] = 0;
            while (copy.Count > 0) {
                //find the minimum hex from copy
                Hex minHex = null;
                int smallestDist = int.MaxValue;
                foreach (Hex cur in copy) {
                    int curDist = dist[cur];
                    if (curDist == 0) {
                        minHex = cur;
                        break;
                    } else if (curDist < smallestDist) {
                        smallestDist = curDist;
                        minHex = cur;
                    }
                }

                if (minHex == null) {
                    throw new IllegalMoveException("There is no clear path to " + destination);
                }

                //extract the minimum from the list
                copy.Remove(minHex);
                if (minHex.Equals(destination)) {
                    //if minHex is the destination, we're done looping through Hexes
                    break;
                }
                //update distance of each neighbor
                foreach (Hex neighbor in minHex.Neighbors) {
                    if (neighbor != null) {
                        int alt = dist[minHex] + mover.MovementCosts[neighbor.Terrain];
                        alt = mover.MovementCosts[neighbor.Terrain] == 0 || neighbor.IsMonsterOn ? int.MaxValue : alt;
                        if (alt < dist[neighbor]) {
                            dist[neighbor] = alt;
                            prev[neighbor] = minHex;
                        }
                    }
                }
            }
            //Now read backwards from destination to source and we have the path!
            List<Hex> path = new List<Hex>();
            Hex hexOnPath = destination;
            while (prev[hexOnPath] != null) {
                path.Add(hexOnPath);
                hexOnPath = prev[hexOnPath];
            }
            //reverse the order so that the source comes first
            path.Reverse();
            return path;
        }
        /// <summary>
        /// Gets all the Hexes within the specified range of the specified center Hex. The range list includes the source Hex.
        /// </summary>
        /// <param name="center">The Hex to use as the center of the range of Hexes.</param>
        /// <param name="range">The number of steps outwards from the center to take; for example, range 2 
        /// returns the center, all Hexes that are the neighbors of the center, and all those Hexes' neighbors.</param>
        /// <returns>A List of Hexes within range of center.</returns>
        public List<Hex> GetHexesInRange(Hex center, int range) {
            return this.GetHexesInRange(center, range, true);
        }
        /// <summary>
        /// Gets all the Hexes within the specified range of the specified center Hex, optionally including or omitting the center Hex.
        /// </summary>
        /// <param name="center">The Hex to use as the center of the range of Hexes.</param>
        /// <param name="range">The number of steps outwards from the center to take; for example, range 2 
        /// includes all Hexes that are the neighbors of the center, and all those Hexes' neighbors.</param>
        /// <param name="includeCenter">If true, the given center Hex will be part of the returned list; if false, it will not.</param>
        /// <returns>A List of Hexes within range of center.</returns>
        public List<Hex> GetHexesInRange(Hex center, int range, bool includeCenter) {
            List<Hex> hexes = new List<Hex>();
            hexes.Add(center);
            for (int i = 1; i <= range; i++) {
                int hexesToUpdate = hexes.Count;
                for (int j = 0; j < hexesToUpdate; j++) {
                    AddNeighbors(hexes[j], hexes);
                }
            }
            if (!includeCenter) { hexes.Remove(center); }
            return hexes;
        }
        /// <summary>
        /// Helper for GetHexesInRange.
        /// </summary>
        /// <param name="center"></param>
        /// <param name="hexList"></param>
        private void AddNeighbors(Hex center, List<Hex> hexList) {
            foreach (Hex n in center.Neighbors) {
                if (n != null && !hexList.Contains(n)) {
                    hexList.Add(n);
                }
            }
        }
        /// <summary>
        /// Ends the current player's turn and begins the next player's turn.
        /// </summary>
        /// <exception cref="InvalidOperationException">Thrown if this method is called outside the appropriate game phase.</exception>
        public List<ControlPoint> EndTurn() {
            if (phase != Phases.Play) { throw new InvalidOperationException("The EndTurn method may only be called when the game phase is Phase.Play."); }
            List<ControlPoint> cps = new List<ControlPoint>();
            
            foreach (Hex hex in _Hexes) {
                if (UpdateControlPoint(hex)) {
                    cps.Add(hex.ControlPoint);
                }
            }

            foreach (Monster m in _Monsters) {
                m.MovesRemaining = m.Speed;
                m.ActionsRemaining = m.ActionsPerTurn;
            }
            active = NextPlayer;
            selected = null;
            target = null;
            attackers.Clear();
            defenders.Clear();

            return cps;
        }
        /// <summary>
        /// Updates the Control Point associated with the given Hex, if there is one.
        /// </summary>
        /// <param name="cp">The Hex whose Control Point should be updated.</param>
        bool UpdateControlPoint(Hex hex) {
            if (!hex.HasControlPoint) { return false; }
            List<Hex> range = GetHexesInRange(hex, 2);
            List<Player> controllers = new List<Player>();
            foreach (Hex h in range) {
                if (h.IsMonsterOn) { controllers.Add(h.MonsterOn.Controller); }
            }
            int previousStrength = hex.ControlPoint.ControlStrength;

            if (controllers.Count == 1) {
                if (hex.ControlPoint.Controller == null) {
                    hex.ControlPoint.Controller = controllers[0];
                    hex.ControlPoint.ControlStrength++;
                } else if (hex.ControlPoint.Controller.Equals(controllers[0])) {
                    hex.ControlPoint.ControlStrength++;
                } else {
                    hex.ControlPoint.ControlStrength--;
                }
            }

            return previousStrength != hex.ControlPoint.ControlStrength;
        }

        /// <summary>
        /// All Monsters that are currently selected or in attackers use one action point to attack the combined force of
        /// the target plus all Monsters in the defenders list. NOTE: you must update the returned dictionary and then call 
        /// CompleteAttack() to actually assign the damage to the appropriate monsters! To update the dictionary,
        /// modify the values to redistribute the damage (initially it is all assigned to the first monster). 
        /// If the total damage done was 0, there is no need to call CompleteAttack (but you may do so, in which case it has no effect).
        /// </summary>
        /// <returns>An AttackResult describing the combat results.</returns>
        public AttackResult BeginAttack() {
            if (target == null || selected == null) {
                throw new InvalidOperationException("You must assign a target and an attacker first.");
            }

            //Initialize random number generator 
            Random rand = new Random();

            //damage will keep track of the damage done by the attacker(s)
            int damage = 0;

            //Now loop through again and attack
            Dictionary<Monster, int> damageReport = new Dictionary<Monster, int>();
            int swarmCount = 0;
            List<Monster> allAttackers = new List<Monster>(attackers);
            allAttackers.Add(selected);

            bool ferocity = false;

            foreach (Monster attacker in allAttackers) {
                //if any attacker has ferocity, defenders will be weakened
                ferocity = (attacker.Abilities & Monster.SpecialAbilities.Ferocity) != 0;

                int attack = 0;
                if ((attacker.Abilities & Monster.SpecialAbilities.Swarm) != 0) {
                    //All attackers add one to their roll
                    attack = rand.Next(1, attacker.Attack + swarmCount * 2 + 1) + 1;

                    //Each swarmer that attacks gives the next swarmer a bigger bonus
                    swarmCount++;
                } else {
                    //Attackers add one to their roll
                    attack = rand.Next(1, attacker.Attack + 1) + 1;
                }
                damage += attack;
                damageReport.Add(attacker, attack);
                attacker.ActionsRemaining--;
            }

            //Now defend
            List<Monster> allDefenders = defenders;
            allDefenders.Add(target);
            foreach (Monster defender in allDefenders) {
                int defense = 0;
                if (ferocity && (defender.Abilities & Monster.SpecialAbilities.Ferocity) == 0) {
                    //-2 because the defender does not have Ferocity but an attacker does.
                    //+1 because the maxValue argument to rand.Next is exclusive but we want it to be inclusive.
                    defense = rand.Next(1, defender.Defense - 2 + 1);
                } else {
                    defense = rand.Next(1, defender.Defense + 1);
                }
                damageReport.Add(defender, defense);
                damage -= defense;
            }

            Dictionary<Monster, int> damageAssignments = new Dictionary<Monster, int>();
            List<Monster> losers = damage > 0 ? allDefenders : allAttackers;

            foreach (Monster t in losers) {
                damageAssignments.Add(t, 0);
            }

            attackResult = new AttackResult(Math.Abs(damage), (damage > 0), damageAssignments, damageReport);
            if (damage == 0) {
                CompleteAttack(damageAssignments);
            }
            return attackResult;
        }

        /// <summary>
        /// Assigns the damage done to the given Monsters. This method trusts that no one has modified the attackResult dictionary keys, only the values. 
        /// Returns the list of losing monsters, each of which has had its health updated. 
        /// </summary>
        /// <param name="damageAssignments">A Dictionary whose keys are Monsters belonging to the team that lost the fight, and whose values are the damage to assign to each monster.</param>
        public List<Monster> CompleteAttack(Dictionary<Monster, int> damageAssignments) {
            List<Monster> retlist = new List<Monster>();
            if (attackResult.TotalDamageDone == 0) {
                attackers.Clear();
                defenders.Clear();
                target = null;
                selected = null;
                return retlist;
            }

            //Check to see if there are errors in the targets list.
            int total = 0;
            foreach (KeyValuePair<Monster, int> kvp in damageAssignments) {
                total += kvp.Value;
                retlist.Add(kvp.Key);
            }
            if (total != attackResult.TotalDamageDone) {
                throw new ArgumentException("The total damage assigned does not add up to the amount of damage done.");
            }

            //Now assign the damage
            foreach (KeyValuePair<Monster, int> kvp in damageAssignments) {
                kvp.Key.Health -= kvp.Value;
            }

            attackers.Clear();
            defenders.Clear();
            target = null;
            selected = null;

            return retlist;
        }

        /// <summary>
        /// The currently selected Monster gains back half its total speed in movement points. Second Move is an action.
        /// </summary>
        /// <exception cref="InvalidOperationException">Thrown if this method is called outside the appropriate game phase.</exception>
        public void SecondMove() {
            if (phase != Phases.Play) { throw new InvalidOperationException("The SecondMove method may only be called when the game phase is Phase.Play."); }

            if (selected == null) {
                throw new IllegalActionException("There is no monster selected.");
            } else if (selected.ActionsRemaining < 1) {
                throw new IllegalActionException(selected + " cannot take any more actions this turn.");
            } else if (!selected.IsAlive) {
                throw new IllegalMoveException("Monster " + selected + " is dead and cannot take actions.");
            }
            selected.ActionsRemaining--;
            selected.MovesRemaining += selected.Speed / 2;
        }

        /// <summary>
        /// Ends the Setup Phases and begins the Play Phases.
        /// </summary>
        public void EndSetupPhase() {
            phase = Phases.Play;
            active = 0;
        }
        /// <summary>
        /// Gives the next player control over the setup phase. When all players are done, call EndSetupPhase.
        /// </summary>
        public void SetupNextPlayer() {
            active = NextPlayer;
        }
    }

    /// <summary>
    /// Encapsulates data related to an attack. Used as the return value of the GameState.BeginAttack method.
    /// </summary>
    [Serializable]
    public class AttackResult
    {
        private Dictionary<Monster, int> assignments;
        /// <summary>
        /// A Dictionary linking Monsters who lost the attack and the damage done to them.
        /// Use this dictionary, after modifying the values to add up to TotalDamageDone, 
        /// as the argument to pass to GameState.CompleteAttack.
        /// </summary>
        public Dictionary<Monster, int> DamageAssignments {
            get { return assignments; }
        }

        private Dictionary<Monster, int> damageDone;

        public Dictionary<Monster, int> DamageDoneByMonsters {
            get { return damageDone; }
        }

        private int damage;
        /// <summary>
        /// The amount of damage done in the attack.
        /// </summary>
        public int TotalDamageDone {
            get { return damage; }
        }

        private bool didAttackersWin;
        /// <summary>
        /// True if the attackers won the fight; false if the defenders won or if it was a tie.
        /// </summary>
        public bool DidAttackersWin {
            get { return didAttackersWin; }
        }

        /// <summary>
        /// Constructs a new AttackResult.
        /// </summary>
        /// <param name="damage">The damage done in the attack.</param>
        /// <param name="didAttackersWin">True if the attackers won, false if the defenders did or it was a tie.</param>
        /// <param name="damageAssignments">A Dictionary linking Monsters who lost the attack and the damage done to them.</param>
        public AttackResult(int damage, bool didAttackersWin, Dictionary<Monster, int> damageAssignments, Dictionary<Monster, int> damageDone) {
            this.damage = damage;
            this.didAttackersWin = didAttackersWin;
            this.assignments = damageAssignments;
            this.damageDone = damageDone;
        }
    }
}
